package com.zbkj.admin.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.zbkj.admin.filter.TokenComponent;
import com.zbkj.admin.model.req.AdminQuotaListReq;
import com.zbkj.admin.model.req.UserOperateQuotaReq;
import com.zbkj.admin.model.req.WithdrawConfirmReq;
import com.zbkj.admin.model.req.WithdrawListReq;
import com.zbkj.admin.model.vo.CalculateAfterBalanceVO;
import com.zbkj.dal.base.constants.SysConfigConstants;
import com.zbkj.dal.entity.system.SystemAttachment;
import com.zbkj.dal.pojo.vo.CloudVo;
import com.zbkj.service.manager.impl.WechatManagerImpl;
import com.zbkj.service.model.vo.QuotaListVO;
import com.zbkj.admin.model.vo.WithdrawListVO;
import com.zbkj.admin.service.AdminFundV2Service;
import com.zbkj.dal.base.constants.OrderConstants;
import com.zbkj.dal.base.enums.*;
import com.zbkj.dal.base.page.CommonPage;
import com.zbkj.dal.common.exception.CrmebException;
import com.zbkj.dal.common.utils.CrmebUtil;
import com.zbkj.dal.common.utils.ExcelUtil;
import com.zbkj.dal.common.utils.MoneyUtil;
import com.zbkj.dal.dao.UserWithdrawConfirmRecordDao;
import com.zbkj.dal.dao.UserWithdrawRecordDao;
import com.zbkj.dal.entity.fund.UserFundAccount;
import com.zbkj.dal.entity.fund.UserQuotaChangeRecord;
import com.zbkj.dal.entity.fund.UserWithdrawConfirmRecord;
import com.zbkj.dal.entity.fund.UserWithdrawRecord;
import com.zbkj.dal.entity.user.UserQuotaAccount;
import com.zbkj.dal.pojo.request.UserOperateBalanceRequest;
import com.zbkj.service.manager.FundManager;
import com.zbkj.service.manager.QuotaManager;
import com.zbkj.service.model.dto.SaveUserBalanceRecordDTO;
import com.zbkj.service.service.SystemConfigService;
import com.zbkj.service.service.impl.FundV2BaseService;
import com.zbkj.service.service.impl.UploadServiceImpl;
import com.zbkj.service.util.DateTimeUtil;
import lombok.SneakyThrows;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.List;
import java.util.Objects;
import java.util.UUID;

@Service
public class AdminFundV2ServiceImpl extends FundV2BaseService implements AdminFundV2Service {

    @Resource
    private FundManager fundManager;

    @Resource
    private QuotaManager quotaManager;

    @Resource
    private TokenComponent tokenComponent;

    @Resource
    private UserWithdrawRecordDao userWithdrawRecordDao;

    @Resource
    private UserWithdrawConfirmRecordDao userWithdrawConfirmRecordDao;

    @Value("${image.default.quota.admin-operate}")
    private String defaultQuotaAdminOperate;

    @Resource
   private UploadServiceImpl uploadService;

    private static final Logger logger = LoggerFactory.getLogger(AdminFundV2ServiceImpl.class);

    @Override
    public CommonPage<WithdrawListVO> withdrawList(WithdrawListReq req) {
        LambdaQueryWrapper<UserWithdrawRecord> wrapper = this.generateWrapper(req);
        PageHelper.startPage(req.getPage(), req.getLimit());
        List<UserWithdrawRecord> userWithdrawRecords = userWithdrawRecordDao.selectList(wrapper);
        PageInfo<UserWithdrawRecord> pageInfo = new PageInfo<>(userWithdrawRecords);
        CommonPage<WithdrawListVO> userWithdrawRecordPage = new CommonPage<>();
        userWithdrawRecordPage.setTotalPage(pageInfo.getPages());
        userWithdrawRecordPage.setPage(pageInfo.getPageNum());
        userWithdrawRecordPage.setLimit(pageInfo.getPageSize());
        userWithdrawRecordPage.setTotal(pageInfo.getTotal());
        userWithdrawRecordPage.setList(WithdrawListVO.transform(userWithdrawRecords));
        return userWithdrawRecordPage;
    }

    @Override
    public void withdrawExport(WithdrawListReq req) {
        LambdaQueryWrapper<UserWithdrawRecord> wrapper = this.generateWrapper(req);
        List<UserWithdrawRecord> userWithdrawRecords = userWithdrawRecordDao.selectList(wrapper);
        List<WithdrawListVO> withdrawListList = WithdrawListVO.transform(userWithdrawRecords);
        ExcelUtil.exportExcel(withdrawListList, WithdrawListVO.class, UUID.randomUUID().toString());
    }

    private LambdaQueryWrapper<UserWithdrawRecord> generateWrapper(WithdrawListReq req) {
        LambdaQueryWrapper<UserWithdrawRecord> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Objects.nonNull(req.getWithdrawNo()), UserWithdrawRecord::getWithdrawNo, req.getWithdrawNo());
        wrapper.eq(Objects.nonNull(req.getFundAccount()), UserWithdrawRecord::getFundAccount, req.getFundAccount());
        wrapper.eq(Objects.nonNull(req.getAccountName()), UserWithdrawRecord::getBankAccountName, req.getAccountName());
        wrapper.in(Objects.nonNull(req.getStatus()), UserWithdrawRecord::getStatus, req.getStatus());
        if (ObjectUtils.allNotNull(req.getCreatedStartDate(), req.getCreatedEndDate())) {
            wrapper.ge(UserWithdrawRecord::getCreateTime, req.getCreatedStartDate().atStartOfDay());
            wrapper.le(UserWithdrawRecord::getCreateTime, LocalDateTime.of(req.getCreatedEndDate(), LocalTime.MAX));
        }
        if (ObjectUtils.allNotNull(req.getFinishedStartDate(), req.getFinishedEndDate())) {
            wrapper.ge(UserWithdrawRecord::getFinishTime, req.getFinishedStartDate().atStartOfDay());
            wrapper.le(UserWithdrawRecord::getFinishTime, LocalDateTime.of(req.getFinishedEndDate(), LocalTime.MAX));
        }
        wrapper.orderByDesc(UserWithdrawRecord::getId);
        return wrapper;
    }

    @Override
    @SneakyThrows
    @Transactional(rollbackFor = Exception.class)
    public void withdrawConfirm(WithdrawConfirmReq req) {
        LambdaQueryWrapper<UserWithdrawRecord> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(UserWithdrawRecord::getWithdrawNo, req.getWithdrawNo());
        wrapper.eq(UserWithdrawRecord::getDeleted, BooleanEnums.FALSE.getCode());
        UserWithdrawRecord userWithdrawRecord = userWithdrawRecordDao.selectOne(wrapper);
        if (Objects.isNull(userWithdrawRecord)) {
            throw new CrmebException("确认失败，未查询到对应的提现记录");
        }
        if (StringUtils.equals(userWithdrawRecord.getStatus(), WithdrawStatusEnums.SUCCESS.getCode())) {
            throw new CrmebException("确认失败，对应的提现记录已是成功状态，请核对");
        }

        String operator = tokenComponent.adminName();

        // 添加确认记录表，这期制作记录，没有重复记录
        UserWithdrawConfirmRecord userWithdrawConfirmRecord = new UserWithdrawConfirmRecord();
        userWithdrawConfirmRecord.setConfirmNo(CrmebUtil.getOrderNo(OrderConstants.USER_WITHDRAW_CONFIRM_ORDER_PREFIX));
        userWithdrawConfirmRecord.setWithdrawNo(req.getWithdrawNo());
        userWithdrawConfirmRecord.setBillDate(DateTimeUtil.todayInt());
        userWithdrawConfirmRecord.setAmount(userWithdrawRecord.getWithdrawAmount());
        userWithdrawConfirmRecord.setPayType(req.getPayType());
        userWithdrawConfirmRecord.setResult(req.getResult());
        userWithdrawConfirmRecord.setRemark(req.getRemark());
        userWithdrawConfirmRecord.setCreateTime(LocalDateTime.now());
        userWithdrawConfirmRecord.setCreatedBy(operator);
        userWithdrawConfirmRecord.setUpdateTime(LocalDateTime.now());
        userWithdrawConfirmRecord.setDeleted(BooleanEnums.FALSE.getCode());

        userWithdrawRecord.setPayType(req.getPayType());
        if (Objects.nonNull(req.getFile())) {
            String path = BizImgTypeEnums.WITHDRAW_CERTIFICATE.path(UUID.randomUUID() + ".jpg");
            uploadService.fileUpload(req.getFile(),"withdraw",9);
            userWithdrawRecord.setCertificatePath(path);
            userWithdrawConfirmRecord.setCertificatePath(path);
        }
        userWithdrawRecord.setOperator(operator);
        userWithdrawRecord.setOperatorRemark(req.getRemark());
        userWithdrawRecord.setStatus(req.getResult());
        userWithdrawRecord.setUpdateTime(LocalDateTime.now());
        userWithdrawRecord.setFinishTime(LocalDateTime.now());
        userWithdrawRecordDao.updateById(userWithdrawRecord);
        userWithdrawConfirmRecordDao.insert(userWithdrawConfirmRecord);
        // 支付失败的返还雅识
        if (!StringUtils.equals(req.getResult(), WithdrawStatusEnums.SUCCESS.getCode())) {
            UserFundAccount userFundAccount = fundManager.addAvailableAmount(userWithdrawRecord.getUserId(), userWithdrawRecord.getWithdrawAmount(), FundAccountType.CRAB_ROE.name());
            SaveUserBalanceRecordDTO saveUserBalanceRecordDTO = SaveUserBalanceRecordDTO.builder()
                    .userFundAccount(userFundAccount).status(FundStatusEnums.FINISH.getCode()).image(FundTradeTypeEnums.WITHDRAW_RETURN.getDefaultImg())
                    .bizOrderNo(userWithdrawRecord.getWithdrawNo()).tradeAmount(userWithdrawRecord.getWithdrawAmount())
                    .fundType(FundTradeTypeEnums.WITHDRAW_RETURN.getCode()).fundDirection(FundDirectionEnums.INCOME.getCode())
                    .remark(String.format("%s %s", userWithdrawRecord.getBankName(), userWithdrawRecord.getBankcardNumber().substring(userWithdrawRecord.getBankcardNumber().length() - 4))).build();
            fundManager.saveUserBalanceRecord(saveUserBalanceRecordDTO);
        }

    }

    @Override
    public CommonPage<QuotaListVO> totalQuotaFundFlow(AdminQuotaListReq req) {
        LambdaQueryWrapper<UserQuotaChangeRecord> wrapper = generateWrapper(req);
        return super.adminQuotaFundFlow(wrapper, req, false);
    }

    @Override
    public CommonPage<QuotaListVO> availableQuotaFundFlow(AdminQuotaListReq req) {
        LambdaQueryWrapper<UserQuotaChangeRecord> wrapper = generateWrapper(req);
        wrapper.in(Objects.nonNull(req.getChangeType()), UserQuotaChangeRecord::getChangeType, QuotaChangeType.levelCalculateList());
        return super.adminQuotaFundFlow(wrapper, req, true);
    }

    private LambdaQueryWrapper<UserQuotaChangeRecord> generateWrapper(AdminQuotaListReq req) {
        LambdaQueryWrapper<UserQuotaChangeRecord> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(StringUtils.isNotBlank(req.getQuotaNo()), UserQuotaChangeRecord::getQuotaNo, req.getQuotaNo());
        wrapper.eq(StringUtils.isNotBlank(req.getBizOrderNo()), UserQuotaChangeRecord::getBizOrderNo, req.getBizOrderNo());
        wrapper.eq(Objects.nonNull(req.getUserId()), UserQuotaChangeRecord::getUserId, req.getUserId());
        if (Objects.nonNull(req.getNickName())) {
        }
        wrapper.eq(Objects.nonNull(req.getChangeType()), UserQuotaChangeRecord::getChangeType, req.getChangeType());
        wrapper.eq(Objects.nonNull(req.getStatus()), UserQuotaChangeRecord::getStatus, req.getStatus());
        wrapper.eq(UserQuotaChangeRecord::getDeleted, BooleanEnums.FALSE.getCode());
        wrapper.ge(Objects.nonNull(req.getBeginDate()), UserQuotaChangeRecord::getBillDate, req.getBeginDate());
        wrapper.ge(Objects.nonNull(req.getEndDate()), UserQuotaChangeRecord::getBillDate, req.getBeginDate());
        wrapper.orderByDesc(UserQuotaChangeRecord::getId);
        return wrapper;
    }


    @Override
    public CalculateAfterBalanceVO calculateAfterBalance(UserOperateBalanceRequest request) {
        UserFundAccount userFundAccount = fundManager.fundAccount(request.getUid(), FundAccountType.CRAB_ROE.name());
        BigDecimal availableAmount = userFundAccount.getAvailableAmount();
        BigDecimal afterBalance;
        if (StringUtils.equals(request.getOperateType(), FundOperateTypeEnums.ADD.getCode())) {
            afterBalance = availableAmount.add(request.getMoney());
        } else {
            afterBalance = availableAmount.subtract(request.getMoney());
        }
        return new CalculateAfterBalanceVO(MoneyUtil.prettyFormat(availableAmount), MoneyUtil.prettyFormat(afterBalance));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean operateQuota(UserOperateQuotaReq request) {
        if (request.getOperateType().equals(FundOperateTypeEnums.ADD.getCode())) {
            quotaManager.addQuota(request.getUid(), request.getMoney(), CrmebUtil.getOrderNo(OrderConstants.SYSTEM_OPERATE_QUOTA_PREFIX),
                    QuotaChangeType.SYSTEM_ADJUSTMENT.getCode(), defaultQuotaAdminOperate, request.getRemark());
        } else {
            quotaManager.minusQuota(request.getUid(), request.getMoney(), CrmebUtil.getOrderNo(OrderConstants.SYSTEM_OPERATE_QUOTA_PREFIX),
                    QuotaChangeType.SYSTEM_ADJUSTMENT.getCode(), defaultQuotaAdminOperate, request.getRemark());
        }
        return true;
    }

    @Override
    public CalculateAfterBalanceVO calculateAfterQuota(UserOperateQuotaReq request) {
        UserQuotaAccount userQuotaAccount = quotaManager.userQuotaAccount(request.getUid());
        BigDecimal quota = StringUtils.equals(request.getQuotaType(), QuotaTypeEnums.TOTAL.getCode()) ?
                userQuotaAccount.getQuotaTotal() : userQuotaAccount.getQuotaBalance();
        BigDecimal afterBalance;
        if (StringUtils.equals(request.getOperateType(), FundOperateTypeEnums.ADD.getCode())) {
            afterBalance = quota.add(request.getMoney());
        } else {
            afterBalance = quota.subtract(request.getMoney());
        }
        return new CalculateAfterBalanceVO(MoneyUtil.prettyFormat(quota), MoneyUtil.prettyFormat(afterBalance));
    }
}
